package sim.core;

import java.util.*;

import sim.gui.*;
import sim.net.euclidean.*;

/**
 * The base class for creating a simulation application.
 * 
 * @author smd
 * 
 */
abstract public class Simulation {

	/**
	 * The global random number generator used throughout the simulator. If
	 * created with a seed e.g., new Random(1234), it is possible to reproduce a
	 * simulation run.
	 */
	public static final Random rg = new Random();
	public static Network Network = new RandomEucledianNetwork();

	protected static final Gui Gui = new Gui();
	private static TimeWarpTask timeWarpTask = null;

	protected Simulation() {
		Gui.addItem( Network);
		Gui.addItem( sim.core.Scheduler.Scheduler);
	}

	/**
	 * Returns the elapsed time since the simulation started.
	 * This is a global clock that should not be used within nodes
	 * for coordination purposes.
	 * The values returned by this method are best used to compute
	 * performance statistics, such as the average number of messages
	 * sent by unit of time...
	 */
	static public double currentTime() {
		return Scheduler.now() ;
	}
	
	/**
	 * Controls the speed of the simulation relative to real time.
	 * For instance, passing 2.0 will cause the simulation to run up to twice as
	 * fast as real time.
	 */
	static public void setSimulationMaxTimeWarp(final double factor) {
		if( timeWarpTask == null )
			timeWarpTask = new TimeWarpTask( factor) ;
		else 
			timeWarpTask.setFactor(factor) ;
	}

	/**
	 * Begins the simulation. The simulation ends when the task scheduler
	 * completes all pending tasks. (Periodic tasks need to be canceled for this
	 * to happen.)
	 * 
	 */
	protected void start() {
		System.err.println("Starting simulation...");
		Gui.start() ;
		Scheduler.start();
		System.err.println("Simulation complete...");
	}

	/**
	 * Explicitly ends the simulation, by canceling all pending tasks. Typically
	 * called when an external observer detects a certain global condition.
	 */
	static protected void stop() {
		Scheduler.cancelAll();
	}

}

class TimeWarpTask extends PeriodicTask {

	double factor;

	TimeWarpTask(double f) {
		super(0.05) ;
		factor = f;
	}

	void setFactor( double f ) {
		if( f != factor ) {
			factor = f ;
			reSchedule(0) ;
		}
	}
	
	public void run() {
		int delay = (int) Math.max(0, 1000 * 0.05 / factor);
		if (delay > 0)
			try { Thread.sleep(delay); } catch (Exception x) {}
		else
			reSchedule( 1e100 ) ;
	}
}